package rpcjson

import (
	"context"
	"encoding/json"
	"github.com/docker/docker/api/types"
	"github.com/pkg/errors"
	"gykjgit.dccnet.com.cn/chain/swarm/client"
	"gykjgit.dccnet.com.cn/chain/swarm/rpcjson/model"
	"io"
	"io/ioutil"
	"strconv"
	"sync"
)

// Container Container
type Container struct{}

func (Container) Logs(req model.ReqContainerLogs, resp *model.RespContainerLogs) (err error) {
	var (
		body io.ReadCloser
		data []byte
	)
	if body, err = client.ObtainClient().ContainerLogs(context.Background(), req.ContainerID, types.ContainerLogsOptions{
		ShowStdout: true,
		ShowStderr: true,
		Timestamps: req.Timestamps,                          // 显示时间戳
		Details:    req.Details,                             // 显示提供给日志的额外细节
		Follow:     req.Follow,                              // 跟踪日志输出
		Tail:       req.Tail,                                // 从日志末尾显示的行数(默认为“all”)
		Since:      strconv.FormatInt(req.Since.Unix(), 10), // "1589772560.000000001"
		Until:      strconv.FormatInt(req.Until.Unix(), 10), // "1589772720.000000001"
	}); nil != err {
		return errors.Wrap(err, "ContainerLogs error")
	}
	defer func() { _ = body.Close() }()
	if data, err = ioutil.ReadAll(body); nil != err {
		return errors.Wrap(err, "io.ReadAll error")
	}
	resp.Data = data
	return nil
}

func (Container) List(req model.ReqContainerList, resp *model.RespContainerList) error {
	containers, err := client.ObtainClient().ContainerList(context.Background(), req.Options)
	if nil != err {
		return errors.Wrap(err, "ContainerList error")
	}
	resp.Containers = containers
	return nil
}

func (Container) Inspect(req model.ReqContainerInspect, resp *model.RespContainerInspect) error {
	containerJson, data, err := client.ObtainClient().ContainerInspectWithRaw(context.Background(), req.ContainerID, req.GetSize)
	if nil != err {
		return errors.Wrap(err, "ContainerInspectWithRaw error")
	}
	resp.ContainerJSON = containerJson
	resp.Data = data
	return nil
}

func (Container) Stats(req model.ReqContainerStats, resp *model.RespContainerStats) error {
	containerStats, err := client.ObtainClient().ContainerStats(context.Background(), req.ContainerID, req.Stream)
	if nil != err {
		return errors.Wrap(err, "ContainerStats error")
	}
	dec := json.NewDecoder(containerStats.Body)
	defer func() { _ = containerStats.Body.Close() }()
	var s *types.Stats
	if err = dec.Decode(&s); nil != err {
		return errors.Wrap(err, "dec.Decode error")
	}
	resp.OSType = containerStats.OSType
	resp.Stats = *s
	return nil
}

func (Container) StatsAll(req model.ReqContainerStatsAll, resp *model.RespContainerStatsAll) error {
	containers, err := client.ObtainClient().ContainerList(context.Background(), req.Options)
	if nil != err {
		return errors.Wrap(err, "ContainerList error")
	}
	var (
		statsAll []model.RespContainerStats
		mu       sync.Mutex
		wg       sync.WaitGroup
	)
	for _, container := range containers {
		wg.Add(1)
		go func(container types.Container) {
			containerStats, errNew := client.ObtainClient().ContainerStats(context.Background(), container.ID, true)
			if nil != errNew {
				err = errors.Wrap(errNew, "ContainerStats error")
				wg.Done()
				return
			}
			dec := json.NewDecoder(containerStats.Body)
			defer func() { _ = containerStats.Body.Close() }()
			var s *types.Stats
			if errNew = dec.Decode(&s); nil != errNew {
				err = errors.Wrap(errNew, "dec.Decode error")
				wg.Done()
				return
			}
			defer mu.Unlock()
			mu.Lock()
			statsAll = append(statsAll, model.RespContainerStats{Stats: *s, OSType: containerStats.OSType})
			wg.Done()
		}(container)
	}
	wg.Wait()
	if nil != err {
		return err
	}
	resp.StatsAll = statsAll
	return nil
}
